package org.winplus.serial.utils;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;


public class SerialPort {
	private static final String TAG = "SerialPort";

	/*
	 * Do not remove or rename the field mFd: it is used by native method
	 * close();
	 */
	private String device;
	private FileDescriptor mFd;
	private FileInputStream mFileInputStream;
	private FileOutputStream mFileOutputStream;

	private byte []mReadBuffer;
	private final int max_len = 128;
	private boolean mOpened = false;
	private ISerialPortCallback mCallback;

	private ReadThread mThread;

	public SerialPort(ISerialPortCallback callback){
		mCallback = callback;
	}

	public boolean openPort(File device, int baudrate, int flags)
			throws SecurityException, IOException {

		/* Check access permission */
		/*if (!device.canRead() || !device.canWrite()) {
			try {
				// Missing read/write permission, trying to chmod the file 
				Process su;
				su = Runtime.getRuntime().exec("/system/bin/su");
				String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
						+ "exit\n";
				su.getOutputStream().write(cmd.getBytes());
				if ((su.waitFor() != 0) || !device.canRead()
						|| !device.canWrite()) {
					throw new SecurityException();
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new SecurityException();
			}
		}*/

		try {
			mFd = open(device.getAbsolutePath(), baudrate, flags);
			if (mFd == null) {
				Log.e(TAG, "native open returns null");
				throw new IOException();
			}
	
			mOpened = true;
	//		mReadBuffer = new byte[max_len];
			mReadBuffer = new byte[max_len];
	
			mFileInputStream = new FileInputStream(mFd);
			mFileOutputStream = new FileOutputStream(mFd);
		} catch (Exception e) {			
			e.printStackTrace();
			return false;
		}

		try {
			mThread = new ReadThread();
			mThread.start();
		} catch (IllegalThreadStateException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	public boolean openPort(String device, int baudrate, int flags)
			throws SecurityException, IOException{
		this.device = device;
		return openPort(new File(device), baudrate, flags);
	}

	public void closePort(){
		try {
			Log.e(TAG, "---------------------------------------InputStream close()");
			mOpened = false;
			mReadBuffer = null;
			if (mFileInputStream != null) mFileInputStream.close();
			if (mFileOutputStream != null) mFileOutputStream.close();
			if (mCallback != null) mCallback = null;
//			mThread.interrupt();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		close();
	}

	public boolean isOpened(){
		return mOpened;
	}

	// Getters and setters
	public InputStream getInputStream() {
		return mFileInputStream;
	}

	public OutputStream getOutputStream() {
		return mFileOutputStream;
	}

	public boolean send(byte []data){
		if (data != null){
			return send(data, 0, data.length);
		}else{
			return false;
		}
	}

	private void sleep(int ms){
		try {
			Thread.sleep(ms);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public boolean send(byte []data, int offset, int len){
		if (mFileOutputStream != null){
			try {
				final int max = 128;
				int cnt = max;
				while (len > 0){
					cnt = Math.min(max, len);
					mFileOutputStream.write(data, offset, cnt);
					mFileOutputStream.flush();
//					DumpBytes("Write", data, offset, cnt);
					sleep(1);
					len -= cnt;
					offset += cnt;
				}
				//mFileOutputStream.write(data, offset, len);

				return true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return false;
	}

	private class ReadThread extends Thread {

		@Override
		public void run() {
			super.run();
			while(mOpened) {
				try
				{
					if (mFileInputStream == null || mReadBuffer == null) return;
					int available = mFileInputStream.available();
					if (available > 0) {
						mReadBuffer = new byte[available];
						int size = mFileInputStream.read(mReadBuffer, 0, available);
						if (mCallback != null) mCallback.onReadData(device, mReadBuffer, 0, size);
					}
					if (this.isInterrupted()) Thread.sleep(5);//延时50ms
				} catch (Exception e) {
					Log.e(TAG, "---------------------------------------InputStream close(1)");
					e.printStackTrace();
					closePort();
					return;
				}
			}
		}

	}

	// JNI
	private native static FileDescriptor open(String path, int baudrate, int flags);
	public native void close();
	static {
		System.loadLibrary("serial_port");
	}
}
